package com.hjq.demo.http.model

import android.app.*
import android.content.*
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.ConnectivityManager
import com.google.gson.JsonSyntaxException
import com.hjq.demo.R
import com.hjq.demo.http.exception.ResultException
import com.hjq.demo.http.exception.TokenException
import com.hjq.gson.factory.GsonFactory
import com.hjq.http.EasyLog
import com.hjq.http.config.IRequestHandler
import com.hjq.http.exception.*
import com.hjq.http.request.HttpRequest
import com.tencent.mmkv.MMKV
import okhttp3.Headers
import okhttp3.Response
import okhttp3.ResponseBody
import java.io.IOException
import java.io.InputStream
import java.lang.reflect.GenericArrayType
import java.lang.reflect.Type
import java.net.SocketTimeoutException
import java.net.UnknownHostException

/**
 *    author : Android 轮子哥
 *    github : https://github.com/getActivity/AndroidProject-Kotlin
 *    time   : 2019/12/07
 *    desc   : 请求处理类
 */
class RequestHandler constructor(private val mApplication: Application) : IRequestHandler {

    private val mmkv: MMKV = MMKV.mmkvWithID("http_cache_id")

    @Throws(Exception::class)
    public override fun requestSuccess(
        httpRequest: HttpRequest<*>,
        response: Response,
        type: Type
    ): Any {
        if (Response::class.java == type) {
            return response
        }

        if (!response.isSuccessful) {
            throw ResponseException(
                String.format(
                    mApplication.getString(R.string.http_response_error),
                    response.code(), response.message()
                ), response
            )
        }

        if (Headers::class.java == type) {
            return response.headers()
        }

        val body = response.body()
            ?: throw NullBodyException(mApplication.getString(R.string.http_response_null_body))

        if (ResponseBody::class.java == type) {
            return body
        }

        // 如果是用数组接收，判断一下是不是用 byte[] 类型进行接收的
        if (type is GenericArrayType) {
            val genericComponentType = type.genericComponentType
            if (Byte::class.javaPrimitiveType == genericComponentType) {
                return body.bytes()
            }
        }

        if (InputStream::class.java == type) {
            return body.byteStream()
        }

        if (Bitmap::class.java == type) {
            return BitmapFactory.decodeStream(body.byteStream())
        }

        val text: String
        try {
            text = body.string()
        } catch (e: IOException) {
            // 返回结果读取异常
            throw DataException(mApplication.getString(R.string.http_data_explain_error), e)
        }

        // 打印这个 Json 或者文本
        EasyLog.printJson(httpRequest, text)

        if (String::class.java == type) {
            return text
        }

        val result: Any

        try {
            result = GsonFactory.getSingletonGson().fromJson(text, type)
        } catch (e: JsonSyntaxException) {
            // 返回结果读取异常
            throw DataException(mApplication.getString(R.string.http_data_explain_error), e)
        }

        if (result is HttpData<*>) {
            val model = result
            val headers = response.headers()
            val headersSize = headers.size()
            val headersMap: MutableMap<String, String> = HashMap(headersSize)
            for (i in 0 until headersSize) {
                headersMap[headers.name(i)] = headers.value(i)
            }

            // Github issue 地址：https://github.com/getActivity/EasyHttp/issues/233
//            model.setResponseHeaders(headersMap);
            if (model.isRequestSucceed()) {
                // 代表执行成功
                return result
            }

            if (model.isTokenFailure()) {
                // 代表登录失效，需要重新登录
                throw TokenException(mApplication.getString(R.string.http_token_error))
            }

            // 代表执行失败
            throw ResultException(model.getMessage(), model)
        }
        return result
    }

    override fun requestFail(httpRequest: HttpRequest<*>, throwable: Throwable): Throwable {
        if (throwable is HttpException) {
            if (throwable is TokenException) {
                // 登录信息失效，跳转到登录页
            }
            return throwable
        }

        if (throwable is SocketTimeoutException) {
            return TimeoutException(
                mApplication.getString(R.string.http_server_out_time),
                throwable
            )
        }

        if (throwable is UnknownHostException) {
            val info =
                (mApplication.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager).activeNetworkInfo
            // 判断网络是否连接
            if (info != null && info.isConnected) {
                // 有连接就是服务器的问题
                return ServerException(
                    mApplication.getString(R.string.http_server_error),
                    throwable
                )
            }
            // 没有连接就是网络异常
            return NetworkException(mApplication.getString(R.string.http_network_error), throwable)
        }

        if (throwable is IOException) {
            // 出现该异常的两种情况
            // 1. 调用 EasyHttp 取消请求
            // 2. 网络请求被中断
            return CancelException(mApplication.getString(R.string.http_request_cancel), throwable)
        }

        return HttpException(throwable.message, throwable)
    }

    override fun downloadFail(httpRequest: HttpRequest<*>, throwable: Throwable): Throwable {
        if (throwable is ResponseException) {
            val responseException = throwable
            val response = responseException.response
            responseException.setMessage(
                String.format(
                    mApplication.getString(R.string.http_response_error),
                    response.code(), response.message()
                )
            )
            return responseException
        } else if (throwable is NullBodyException) {
            val nullBodyException = throwable
            nullBodyException.setMessage(mApplication.getString(R.string.http_response_null_body))
            return nullBodyException
        } else if (throwable is FileMd5Exception) {
            val fileMd5Exception = throwable
            fileMd5Exception.setMessage(mApplication.getString(R.string.http_response_md5_error))
            return fileMd5Exception
        }
        return requestFail(httpRequest, throwable)
    }

    override fun readCache(httpRequest: HttpRequest<*>, type: Type, cacheTime: Long): Any? {
        val cacheKey: String = HttpCacheManager.generateCacheKey(httpRequest)
        val cacheValue: String? = HttpCacheManager.readHttpCache(cacheKey)
        if (cacheValue == null || "" == cacheValue || "{}" == cacheValue) {
            return null
        }
        EasyLog.printLog(httpRequest, "----- read cache key -----")
        EasyLog.printJson(httpRequest, cacheKey)
        EasyLog.printLog(httpRequest, "----- read cache value -----")
        EasyLog.printJson(httpRequest, cacheValue)
        EasyLog.printLog(httpRequest, "cacheTime = $cacheTime")
        val cacheInvalidate: Boolean = HttpCacheManager.isCacheInvalidate(cacheKey, cacheTime)
        EasyLog.printLog(httpRequest, "cacheInvalidate = $cacheInvalidate")
        if (cacheInvalidate) {
            // 表示缓存已经过期了，直接返回 null 给外层，表示缓存不可用
            return null
        }
        return GsonFactory.getSingletonGson().fromJson(cacheValue, type)
    }

    override fun writeCache(httpRequest: HttpRequest<*>, response: Response, result: Any): Boolean {
        val cacheKey: String = HttpCacheManager.generateCacheKey(httpRequest)
        val cacheValue = GsonFactory.getSingletonGson().toJson(result)
        if (cacheValue == null || "" == cacheValue || "{}" == cacheValue) {
            return false
        }
        EasyLog.printLog(httpRequest, "----- write cache key -----")
        EasyLog.printJson(httpRequest, cacheKey)
        EasyLog.printLog(httpRequest, "----- write cache value -----")
        EasyLog.printJson(httpRequest, cacheValue)
        val writeHttpCacheResult: Boolean = HttpCacheManager.writeHttpCache(cacheKey, cacheValue)
        EasyLog.printLog(httpRequest, "writeHttpCacheResult = $writeHttpCacheResult")
        val refreshHttpCacheTimeResult: Boolean =
            HttpCacheManager.setHttpCacheTime(cacheKey, System.currentTimeMillis())
        EasyLog.printLog(httpRequest, "refreshHttpCacheTimeResult = $refreshHttpCacheTimeResult")
        return writeHttpCacheResult && refreshHttpCacheTimeResult
    }

    override fun clearCache() {
        HttpCacheManager.clearCache()
    }
}